//(function() {
//	
//	Renderer = function(canvas) {
//		var canvas = $(canvas).get(0)
//		var ctx = canvas.getContext("2d");
//		var gfx = arbor.Graphics(canvas)
//		var particleSystem = null
//
//		var that = {
//			init : function(system) {
//				particleSystem = system
//				particleSystem.screenSize(canvas.width, canvas.height)
//				particleSystem.screenPadding(40)
//
//				that.initMouseHandling()
//			},
//			
//			redraw : function() {
//				if (!particleSystem)
//					return
//
//				
//
//				gfx.clear() // convenience ƒ: clears the whole canvas rect
//				
//				// draw the nodes & save their bounds for edge drawing
//				var nodeBoxes = {}
//				particleSystem.eachNode(function(node, pt) {
//					// node: {mass:#, p:{x,y}, name:"", data:{}}
//					// pt: {x:#, y:#} node position in screen coords
//					
//					// determine the box size and round off the coords if we'll
//					// be
//					// drawing a text label (awful alignment jitter
//					// otherwise...)
//					var label = node.data.label || ""
//					var w = ctx.measureText("" + label).width + 10
//					if (!("" + label).match(/^[ \t]*$/)) {
//						pt.x = Math.floor(pt.x)
//						pt.y = Math.floor(pt.y)
//					} else {
//						label = null
//					}
//					
//					// draw a rectangle centered at pt
//					if (node.data.color)
//						ctx.fillStyle = node.data.color
//					else
//						ctx.fillStyle = "rgba(0,0,0,.2)"
//					if (node.data.color == 'none')
//						ctx.fillStyle = "white"
//
//					if (node.data.shape == 'dot') {
//						gfx.oval(pt.x - w / 2, pt.y - w / 2, w, w, {
//							fill : ctx.fillStyle
//						})
//						nodeBoxes[node.name] = [ pt.x - w / 2, pt.y - w / 2, w, w ]
//					} else {
//						gfx.rect(pt.x - w / 2, pt.y - 10, w, 20, 4, {
//							fill : ctx.fillStyle
//						})
//						nodeBoxes[node.name] = [ pt.x - w / 2, pt.y - 11, w, 22 ]
//					}
//					
//					// draw the text
//					if (label) {
//						ctx.font = "12px Helvetica"
//						ctx.textAlign = "center"
//						ctx.fillStyle = "white"
//						if (node.data.color == 'none')
//							ctx.fillStyle = '#333333'
//						ctx.fillText(label || "", pt.x, pt.y + 4)
//						ctx.fillText(label || "", pt.x, pt.y + 4)
//					}
//				})
//
//				// draw the edges
//				particleSystem.eachEdge(function(edge, pt1, pt2) {
//					// edge: {source:Node, target:Node, length:#, data:{}}
//					// pt1: {x:#, y:#} source position in screen coords
//					// pt2: {x:#, y:#} target position in screen coords
//					
//					var weight = edge.data.weight
//					var color = edge.data.color
//
//					if (!color || ("" + color).match(/^[ \t]*$/))
//						color = null
//
//						// find the start point
//					var tail = intersect_line_box(pt1, pt2, nodeBoxes[edge.source.name])
//					var head = intersect_line_box(tail, pt2, nodeBoxes[edge.target.name])
//
//					ctx.save()
//					ctx.beginPath()
//					ctx.lineWidth = (!isNaN(weight)) ? parseFloat(weight) : 1
//					ctx.strokeStyle = (color) ? color : "#cccccc"
//					ctx.fillStyle = null
//
//					ctx.moveTo(tail.x, tail.y)
//					ctx.lineTo(head.x, head.y)
//					ctx.stroke()
//					ctx.restore()
//
//					// draw an arrowhead if this is a -> style edge
//					if (edge.data.directed) {
//						ctx.save()
//						// move to the head position of the edge we just drew
//						var wt = !isNaN(weight) ? parseFloat(weight) : 1
//						var arrowLength = 6 + wt
//						var arrowWidth = 2 + wt
//						ctx.fillStyle = (color) ? color : "#cccccc"
//						ctx.translate(head.x, head.y);
//						ctx.rotate(Math.atan2(head.y - tail.y, head.x - tail.x));
//						
//						// delete some of the edge that's already there (so the
//						// point isn't hidden)
//						ctx.clearRect(-arrowLength / 2, -wt / 2, arrowLength / 2, wt)
//
//						// draw the chevron
//						ctx.beginPath();
//						ctx.moveTo(-arrowLength, arrowWidth);
//						ctx.lineTo(0, 0);
//						ctx.lineTo(-arrowLength, -arrowWidth);
//						ctx.lineTo(-arrowLength * 0.8, -0);
//						ctx.closePath();
//						ctx.fill();
//						ctx.restore()
//					}
//				})
//
//			},
//			initMouseHandling : function() {
//				// no-nonsense drag and drop (thanks springy.js)
//				selected = null;
//				nearest = null;
//				var dragged = null;
//				var oldmass = 1
//
//				// set up a handler object that will initially listen for
//				// mousedowns then
//				// for moves and mouseups while dragging
//				var handler = {
//					clicked : function(e) {
//						var pos = $(canvas).offset();
//						_mouseP = arbor.Point(e.pageX - pos.left, e.pageY - pos.top)
//						selected = nearest = dragged = particleSystem.nearest(_mouseP);
//						
//						if (dragged.node !== null)
//							dragged.node.fixed = true
//
//						$(canvas).bind('mousemove', handler.dragged)
//						$(window).bind('mouseup', handler.dropped)
//
//						return false
//					},
//					dragged : function(e) {
//						var old_nearest = nearest && nearest.node._id
//						var pos = $(canvas).offset();
//						var s = arbor.Point(e.pageX - pos.left, e.pageY - pos.top)
//
//						if (!nearest)
//							return
//
//						if (dragged !== null && dragged.node !== null) {
//							var p = particleSystem.fromScreen(s)
//							dragged.node.p = p
//						}
//						
//						return false
//					},
//					
//					dropped : function(e) {
//						if (dragged === null || dragged.node === undefined)
//							return
//
//						if (dragged.node !== null)
//							dragged.node.fixed = false
//						dragged.node.tempMass = 50
//						dragged = null
//						selected = null
//						$(canvas).unbind('mousemove', handler.dragged)
//						$(window).unbind('mouseup', handler.dropped)
//						_mouseP = null
//						return false
//					}
//				}
//				$(canvas).mousedown(handler.clicked);
//				
//			}
//		
//		}
//
//		// helpers for figuring out where to draw arrows (thanks springy.js)
//		var intersect_line_line = function(p1, p2, p3, p4) {
//			var denom = ((p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y));
//			if (denom === 0)
//				return false // lines are parallel
//			var ua = ((p4.x - p3.x) * (p1.y - p3.y) - (p4.y - p3.y) * (p1.x - p3.x)) / denom;
//			var ub = ((p2.x - p1.x) * (p1.y - p3.y) - (p2.y - p1.y) * (p1.x - p3.x)) / denom;
//			
//			if (ua < 0 || ua > 1 || ub < 0 || ub > 1)
//				return false
//			return arbor.Point(p1.x + ua * (p2.x - p1.x), p1.y + ua * (p2.y - p1.y));
//		}
//
//		var intersect_line_box = function(p1, p2, boxTuple) {
//			var p3 = {
//				x : boxTuple[0],
//				y : boxTuple[1]
//			}, w = boxTuple[2], h = boxTuple[3]
//
//			var tl = {
//				x : p3.x,
//				y : p3.y
//			};
//			var tr = {
//				x : p3.x + w,
//				y : p3.y
//			};
//			var bl = {
//				x : p3.x,
//				y : p3.y + h
//			};
//			var br = {
//				x : p3.x + w,
//				y : p3.y + h
//			};
//			
//			return intersect_line_line(p1, p2, tl, tr) || intersect_line_line(p1, p2, tr, br) || intersect_line_line(p1, p2, br, bl) || intersect_line_line(p1, p2, bl, tl) || false
//		}
//
//		return that
//	}
//
//})()

Renderer = function(canvas) {
	var canvas = $(canvas).get(0)
	var ctx = canvas.getContext("2d");
	var gfx = arbor.Graphics(canvas)
	var particleSystem = null

	// helpers for figuring out where to draw arrows (thanks springy.js)
	var intersect_line_line = function(p1, p2, p3, p4) {
		var denom = ((p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y));
		if (denom === 0)
			return false // lines are parallel
		var ua = ((p4.x - p3.x) * (p1.y - p3.y) - (p4.y - p3.y) * (p1.x - p3.x)) / denom;
		var ub = ((p2.x - p1.x) * (p1.y - p3.y) - (p2.y - p1.y) * (p1.x - p3.x)) / denom;
		
		if (ua < 0 || ua > 1 || ub < 0 || ub > 1)
			return false
		return arbor.Point(p1.x + ua * (p2.x - p1.x), p1.y + ua * (p2.y - p1.y));
	}

	var intersect_line_box = function(p1, p2, boxTuple) {
		var p3 = {
			x : boxTuple[0],
			y : boxTuple[1]
		}, w = boxTuple[2], h = boxTuple[3]

		var tl = {
			x : p3.x,
			y : p3.y
		};
		var tr = {
			x : p3.x + w,
			y : p3.y
		};
		var bl = {
			x : p3.x,
			y : p3.y + h
		};
		var br = {
			x : p3.x + w,
			y : p3.y + h
		};
		
		return intersect_line_line(p1, p2, tl, tr) || intersect_line_line(p1, p2, tr, br) || intersect_line_line(p1, p2, br, bl) || intersect_line_line(p1, p2, bl, tl) || false
	}

	var that = {
		//
		// the particle system will call the init function once, right before
		// the
		// first frame is to be drawn. it's a good place to set up the canvas
		// and
		// to pass the canvas size to the particle system
		//
		init : function(system) {
			// save a reference to the particle system for use in the .redraw()
			// loop
			particleSystem = system

			particleSystem.screenSize(canvas.width, canvas.height)
			particleSystem.screenPadding(40) // leave an extra 20px of
			
			that.initMouseHandling()
		},
		
		redraw : function() {
			if (!particleSystem)
				return;
			
			ctx.clearRect(0, 0, canvas.width, canvas.height)

			var nodeBoxes = {}
			particleSystem.eachNode(function(node, pt) {
				// node: {mass:#, p:{x,y}, name:"", data:{}}
				// pt: {x:#, y:#} node position in screen coords
				
				// determine the box size and round off the coords if we'll be
				// drawing a text label (awful alignment jitter otherwise...)
				var label = node.data.label || ""
				var w = ctx.measureText("" + label).width + 10
				if (!("" + label).match(/^[ \t]*$/)) {
					pt.x = Math.floor(pt.x)
					pt.y = Math.floor(pt.y)
				} else {
					label = null
				}
				
				// draw a rectangle centered at pt
				if (node.data.color)
					ctx.fillStyle = node.data.color
					// else ctx.fillStyle = "#d0d0d0"
				else
					ctx.fillStyle = "rgba(0,0,0,.2)"
				if (node.data.color == 'none')
					ctx.fillStyle = "white"

					// ctx.fillRect(pt.x-w/2, pt.y-10, w,20)
				if (node.data.shape == 'dot') {
					gfx.oval(pt.x - w / 2, pt.y - w / 2, w, w, {
						fill : ctx.fillStyle
					})
					nodeBoxes[node.name] = [ pt.x - w / 2, pt.y - w / 2, w, w ]
				} else {
					gfx.rect(pt.x - w / 2, pt.y - 10, w, 20, 4, {
						fill : ctx.fillStyle
					})
					nodeBoxes[node.name] = [ pt.x - w / 2, pt.y - 11, w, 22 ]
				}
				
				// w = Math.max(20,w)
				
				// draw the text
				if (label) {
					ctx.font = "12px Helvetica"
					ctx.textAlign = "center"
					ctx.fillStyle = "white"
					if (node.data.color == 'none')
						ctx.fillStyle = '#333333'
					if (node.data.fontColor != 'none') {
						ctx.fillStyle = node.data.fontColor
					}
					ctx.fillText(label || "", pt.x, pt.y + 4)
					ctx.fillText(label || "", pt.x, pt.y + 4)
				}
				var texto = node.data.costo
				if (!texto || texto == false) {
					return;
				}
				if (texto == false || !node.data.item._costo) {
					texto = "Costo: 0"
				} else {
					texto = "Costo: " + node.data.item._costo
				}
				w = ctx.measureText("" + texto).width + 15
				gfx.rect(pt.x - w / 2, pt.y + 13, w, 23, 4, {
					fill : '#35d4ec'
				})
				ctx.font = "14px Helvetica"
				ctx.textAlign = "center"
				ctx.fillStyle = "white"
				ctx.fillText(texto || "", pt.x, pt.y + 30)
				ctx.fillText(texto || "", pt.x, pt.y + 30)
			})

			ctx.strokeStyle = "#cccccc"
			ctx.lineWidth = 1
			ctx.beginPath()
			particleSystem.eachEdge(function(edge, pt1, pt2) {
				// edge: {source:Node, target:Node, length:#, data:{}}
				// pt1: {x:#, y:#} source position in screen coords
				// pt2: {x:#, y:#} target position in screen coords
				
				var weight = edge.data.weight
				var color = edge.data.color
				var label = edge.data.label || ""
				// trace(color)
				if (!color || ("" + color).match(/^[ \t]*$/))
					color = null

					// find the start point
				var tail = intersect_line_box(pt1, pt2, nodeBoxes[edge.source.name])
				var head = intersect_line_box(tail, pt2, nodeBoxes[edge.target.name])

				ctx.save()
				ctx.beginPath()

				if (!isNaN(weight))
					ctx.lineWidth = weight
				if (color)
					ctx.strokeStyle = color
					// if (color) trace(color)
				ctx.fillStyle = null

				ctx.moveTo(tail.x, tail.y)
				ctx.lineTo(head.x, head.y)
				ctx.stroke()
				ctx.restore()

				if (label) {
					ctx.font = "12px Helvetica"
					ctx.textAlign = "center"
					ctx.fillStyle = "black"
					if (edge.data.color == 'none')
						ctx.fillStyle = '#000000'
					ctx.fillText(label || "", (head.x + tail.x) / 2, (head.y + tail.y) / 2)
					ctx.fillText(label || "", (head.x + tail.x) / 2, (head.y + tail.y) / 2)
				}
				
				// draw an arrowhead if this is a -> style edge
				if (edge.data.directed) {
					ctx.save()
					// move to the head position of the edge we just drew
					var wt = !isNaN(weight) ? parseFloat(weight) : ctx.lineWidth
					var arrowLength = 6 + wt
					var arrowWidth = 2 + wt
					ctx.fillStyle = (color) ? color : ctx.strokeStyle
					ctx.translate(head.x, head.y);
					ctx.rotate(Math.atan2(head.y - tail.y, head.x - tail.x));
					
					// delete some of the edge that's already there (so the
					// point isn't hidden)
					ctx.clearRect(-arrowLength / 2, -wt / 2, arrowLength / 2, wt)

					// draw the chevron
					ctx.beginPath();
					ctx.moveTo(-arrowLength, arrowWidth);
					ctx.lineTo(0, 0);
					ctx.lineTo(-arrowLength, -arrowWidth);
					ctx.lineTo(-arrowLength * 0.8, -0);
					ctx.closePath();
					ctx.fill();
					ctx.restore()
				}
			})

		},
		initMouseHandling : function() {
			// no-nonsense drag and drop (thanks springy.js)
			selected = null;
			nearest = null;
			var dragged = null;
			var oldmass = 1;
			var ultimo = null;
			
			var handler = {
				clicked : function(e) {
					var pos = $(canvas).offset();
					_mouseP = arbor.Point(e.pageX - pos.left, e.pageY - pos.top)
					selected = nearest = dragged = particleSystem.nearest(_mouseP);
					
					if (dragged.node !== null) {
						dragged.node.fixed = true
						// if (selected.node !== null) dragged.node.tempMass =
						// 10000
						if (ultimo != null) {
							ultimo.node.data.costo = false
							ultimo = null
						}
						if (dragged != null) {
							ultimo = dragged;
							ultimo.node.data.costo = true
						}
					}
					
					$(canvas).bind('mousemove', handler.dragged)
					$(window).bind('mouseup', handler.dropped)

					return false
				},
				dragged : function(e) {
					var old_nearest = nearest && nearest.node._id
					var pos = $(canvas).offset();
					var s = arbor.Point(e.pageX - pos.left, e.pageY - pos.top)

					if (!nearest)
						return;
					
					if (dragged !== null && dragged.node !== null) {
						var p = particleSystem.fromScreen(s)
						dragged.node.p = p// {x:p.x, y:p.y}
						// dragged.tempMass = 100000
					}
					
					return false
				},
				
				dropped : function(e) {
					if (dragged === null || dragged.node === undefined)
						return;
					
					if (dragged.node !== null)
						dragged.node.fixed = false
					dragged.node.tempMass = 1000
					dragged = null;
					selected = null
					$(canvas).unbind('mousemove', handler.dragged)
					$(window).unbind('mouseup', handler.dropped)
					_mouseP = null
					return false
				}
			
			}

			$(canvas).mousedown(handler.clicked);
			
		},
	
	}
	return that
}